#include "lib/common.h"

#define  MAXLINE     1024

int main(int argc, char **argv)
{
    if (argc != 3)
    {
        error(1, 0, "usage: tcp_client <IPaddress> <port>");
    }

    int port = atoi(argv[2]);
    int socket_fd = tcp_client(argv[1], port);

    char recv_line[MAXLINE], send_line[MAXLINE];
    int n;

    //select()机制中提供一fd_set的数据结构，实际上是一long类型的数组，
    //每一个数组元素都能与一打开的文件句柄（不管是socket句柄，还是其他文件或命名管道或设备句柄）建立联系，建立联系的工作由程序员完成，
    //当调用select()时，由内核根据IO状态修改fd_set的内容，由此来通知执行了select()的进程哪一socket或文件发生了可读或可写事件。
    fd_set readmask;
    fd_set allreads;
    FD_ZERO(&allreads);/*将set清零使集合中不含任何fd*/
    FD_SET(0, &allreads);
    FD_SET(socket_fd, &allreads);/*将socket_fd加入set集合*/

    for (;;)
    {
        readmask = allreads;

        //如果timeout==NULL，无期限等待下去，这个等待可以被一个信号中断，
        //只有当一个描述符准备好，或者捕获到一个信号时函数才会返回。如果是捕获到信号，select返回-1，并将变量errno设置成EINTR
        //成功时：返回三中描述符集合中”准备好了“的文件描述符数量。
        //超时：返回0
        //错误：返回-1，并设置 errno
        int rc = select(socket_fd + 1, &readmask, NULL, NULL, NULL);

        if (rc <= 0)
        {
            error(1, errno, "select failed");
        }

        if (FD_ISSET(socket_fd, &readmask))
        {
            n = read(socket_fd, recv_line, MAXLINE);

            if (n < 0)
            {
                error(1, errno, "read error");
            }
            else if (n == 0)
            {
                printf("server closed \n");
                break;
            }

            recv_line[n] = 0;
            fputs(recv_line, stdout);
            fputs("\n", stdout);
        }

        //操作系统一级提供的文件API都是以文件描述符来表示文件。STDIN_FILENO就是标准输入设备（一般是键盘）的文件描述符。
        if (FD_ISSET(STDIN_FILENO, &readmask))
        {
            if (fgets(send_line, MAXLINE, stdin) != NULL)
            {
                int i = strlen(send_line);
                if (send_line[i - 1] == '\n')
                {
                    send_line[i - 1] = 0;
                }

                if (strncmp(send_line, "quit", strlen(send_line)) == 0)
                {
                    //0:关闭读通道 1:关闭写通道 2:关闭读写通道（假如有几个子进程共享一个套接字时,假如我们使用shutdown, 那么任何的子进程都不能够操作了）
                    if (shutdown(socket_fd, 1))
                    {
                        error(1, errno, "shut down failed");
                    }
                }

                size_t rt = write(socket_fd, send_line, strlen(send_line));
                if (rt < 0)
                {
                    error(1, errno, "write failed");
                }
            }
        }
    }

    exit(0);
}